package net.csdn.business.mirror.pop.service.thirdapi.impl;

import cn.hutool.json.JSONUtil;
import com.google.common.base.Splitter;
import lombok.extern.slf4j.Slf4j;
import net.csdn.business.common.enums.ResultCodeEnum;
import net.csdn.business.common.exception.BusinessException;
import net.csdn.business.mirror.pop.bo.UserBO;
import net.csdn.business.mirror.pop.dto.MyGithubDto;
import net.csdn.business.mirror.pop.enums.UserTokenEnum;
import net.csdn.business.mirror.pop.service.thirdapi.ThirdGitApi;
import net.csdn.business.mirror.pop.vo.UserTokenVo;
import net.csdn.business.mirror.utils.HttpUtil;
import net.csdn.business.mirror.utils.StringUtil;
import net.csdn.business.mirror.utils.UrlUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Service
@Slf4j
@Primary
public class ThirdGithubGitApiImpl implements ThirdGitApi {

    // https://docs.github.com/zh/rest/users/users?apiVersion=2022-11-28
    @Value("${githubApi.userUrl:https://api.github.com/user}")
    private String userUrl;

    // https://docs.github.com/zh/rest/repos/repos?apiVersion=2022-11-28#list-repositories-for-the-authenticated-user
    @Value("${githubApi.userAllReposUrl:https://api.github.com/user/repos?sort=updated&direction=desc&per_page=100&visibility=public}")
    private String userAllReposUrl;

    // https://docs.github.com/en/rest/orgs/orgs?apiVersion=2022-11-28#list-organizations-for-the-authenticated-user
    @Value("${githubApi.userAllOrgsUrl:https://api.github.com/user/orgs?page=1&per_page=100}")
    private String userAllOrgsUrl;

    /**
     * 应用通过 浏览器 或 Webview 将用户引导到码云三方认证页面上
     */
    @Value("${githubApi.authorize:https://github.com/login/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&allow_signup={allow_signup}&state={state}}")
    private String authorizeUrl;
    /**
     * 根据code获取access_token
     */
    @Value("${githubApi.token:https://github.com/login/oauth/access_token?grant_type=authorization_code&code={code}&client_id={client_id}&redirect_uri={redirect_uri}&client_secret={client_secret}}")
    private String tokenUrl;

    @Value("${githubApi.client_id:Ov23liFEwkM3gUE08jrJ}")
    private String client_id;

    @Value("${githubApi.client_secret:9e22ede03c19828d283ca6a2a3579f7208f66167}")
    private String client_secret;

    @Value("${githubApi.redirect_uri:https://test.atomgit.com/-/others/import/callback}")
    private String redirect_uri;

    @Value("${githubApi.apiVersion:2022-11-28}")
    private String apiVersion;

    @Value("${githubApi.apiAccept:application/vnd.github+json}")
    private String apiAccept;

    @Override
    public UserBO getUser(String token) {
        if (StringUtils.isEmpty(token)) {
            return null;
        }
        String content = HttpUtil.httpRequest(HttpUtil.RequestMethodEnum.GET, userUrl, buildHeader(token), null, null).getContent();
        UserBO user = JSONUtil.toBean(content, UserBO.class);
        if (Objects.isNull(user) || StringUtils.isEmpty(user.getLogin())) {
            // {"message":"Bad credentials","documentation_url":"https://docs.github.com/rest"}
            // {"message":"Not Found","documentation_url":"https://docs.github.com/rest/reference/users/#update-the-authenticated-user"}
            log.error("GithubApi getUser failed: {}", content);
            return null;
        }
        return user;
    }

    @Override
    public void listUserAllRepos(MyGithubDto myGithub, Integer page, Integer pageSize) {
        if (StringUtils.isEmpty(myGithub.getToken())) {
            return;
        }
        if (Objects.isNull(page) || page < 1) {
            page = 1;
        }
        if (Objects.isNull(pageSize) || pageSize < 1 || pageSize > 100) {
            pageSize = 100;
        }
        Map<String, String> params = new HashMap<>();
        params.put("page", page.toString());
        params.put("per_page", pageSize.toString());
        params.put("visibility", "public");
        String url = UrlUtil.getNewUrl(userAllReposUrl, params);
        HttpUtil.Response response = HttpUtil.httpRequest(HttpUtil.RequestMethodEnum.GET, url, buildHeader(myGithub.getToken()), null, null, 10000);
        String content = response.getContent();
        int code = response.getCode();
        if (code != 200) {
            switch (code) {
                case 401:
                case 403:
                    log.error("GithubApi listUserAllRepos  token 无效: {}", content);
                    throw new BusinessException(ResultCodeEnum.MIRROR_GITHUB_TOKEN_INVALID);
                default:
                    log.error("GithubApi listUserAllRepos token failed reason: {}", content);
                    break;
            }
            return;
        }
        List<MyGithubDto.Repository> repos = JSONUtil.toList(content, MyGithubDto.Repository.class);
        repos.forEach(repo -> {
            String tml_url = repo.getHtml_url();
            String web_url = repo.getWeb_url();
            if (StringUtils.isEmpty(tml_url)) {
                repo.setHtml_url(web_url);
            }
            if (StringUtils.isEmpty(web_url)) {
                repo.setWeb_url(tml_url);
            }
        });
        myGithub.setItems(repos);
        String link = response.getHeaders().getOrDefault("Link", "");
        myGithub.setThirdTotalPage(1);
        if (!StringUtils.isEmpty(link)) {
            if (link.contains(">; rel=\"last\"")) {
                String[] urls = link.split(">; rel=\\\"last\\\"")[0].split("\\?");
                String lastParameterStr = urls[urls.length - 1];
                Map<String, String> lastParameters = Splitter.on("&").withKeyValueSeparator("=").split(lastParameterStr);
                if (lastParameters.containsKey("page")) {
                    myGithub.setThirdTotalPage(Integer.parseInt(lastParameters.get("page")));
                }
            } else if (link.contains(">; rel=\"prev\"")) {
                String[] urls = link.split(">; rel=\\\"prev\\\"")[0].split("\\?");
                String lastParameterStr = urls[urls.length - 1];
                Map<String, String> lastParameters = Splitter.on("&").withKeyValueSeparator("=").split(lastParameterStr);
                if (lastParameters.containsKey("page")) {
                    myGithub.setThirdTotalPage(Integer.parseInt(lastParameters.get("page")) + 1);
                }
            }
        }
        myGithub.setTotalCount(Integer.parseInt(response.getHeaders().getOrDefault("total_count", String.valueOf(myGithub.getThirdTotalPage()*pageSize))));
    }

    @Override
    public void listUserAllOrgs(MyGithubDto myGithub, Integer page, Integer pageSize) {
        if (StringUtils.isEmpty(myGithub.getToken())) {
            throw new BusinessException(ResultCodeEnum.MIRROR_GITHUB_TOKEN_INVALID);
        }
        Map<String, String> params = new HashMap<>();
        params.put("page", page.toString());
        params.put("per_page", pageSize.toString());
        String url = UrlUtil.getNewUrl(userAllOrgsUrl, params);
        HttpUtil.Response response = HttpUtil.httpRequest(HttpUtil.RequestMethodEnum.GET, url, buildHeader(myGithub.getToken()), null, null, 10000);
        String content = response.getContent();
        int code = response.getCode();
        if (code != 200) {
            log.error("GithubApi listUserNamespaces  failed reason: {}", content);
            throw new BusinessException(ResultCodeEnum.MIRROR_GITHUB_TOKEN_INVALID);
        }
        List<MyGithubDto.Organization> organizations = JSONUtil.toList(content, MyGithubDto.Organization.class);
        organizations.forEach(org -> {
            org.setUrl(UserTokenEnum.Type.GITHUB.getBaseUrl()+org.getLogin());
            org.setHtml_url(UserTokenEnum.Type.GITHUB.getBaseUrl()+org.getLogin());
            org.setPath(org.getLogin());
        });
        myGithub.setOrganizations(organizations);
        String link = response.getHeaders().getOrDefault("Link", "");
        myGithub.setThirdTotalPage(1);
        if (!StringUtils.isEmpty(link)) {
            if (link.contains(">; rel=\"last\"")) {
                String[] urls = link.split(">; rel=\\\"last\\\"")[0].split("\\?");
                String lastParameterStr = urls[urls.length - 1];
                Map<String, String> lastParameters = Splitter.on("&").withKeyValueSeparator("=").split(lastParameterStr);
                if (lastParameters.containsKey("page")) {
                    myGithub.setThirdTotalPage(Integer.parseInt(lastParameters.get("page")));
                }
            } else if (link.contains(">; rel=\"prev\"")) {
                String[] urls = link.split(">; rel=\\\"prev\\\"")[0].split("\\?");
                String lastParameterStr = urls[urls.length - 1];
                Map<String, String> lastParameters = Splitter.on("&").withKeyValueSeparator("=").split(lastParameterStr);
                if (lastParameters.containsKey("page")) {
                    myGithub.setThirdTotalPage(Integer.parseInt(lastParameters.get("page")) + 1);
                }
            }
        }
        myGithub.setTotalCount(Integer.parseInt(response.getHeaders().getOrDefault("total_count", String.valueOf(myGithub.getThirdTotalPage()*pageSize))));
    }


    @Override
    public String authorize() {
        Map<String, Object> parameter = new HashMap<>();
        parameter.put("client_id", client_id);
        parameter.put("redirect_uri", redirect_uri);
        return StringUtil.replace(authorizeUrl, parameter);
    }

    @Override
    public UserTokenVo getToken(String code) {
        Map<String, Object> parameter = new HashMap<>();
        parameter.put("code", code);
        parameter.put("client_id", client_id);
        parameter.put("redirect_uri", redirect_uri);
        parameter.put("client_secret", client_secret);
        String url = StringUtil.replace(tokenUrl, parameter);

        Map<String, String> headers = new HashMap<>();
        headers.put("Accept", "application/json;charset=UTF-8");
        try {
            HttpUtil.Response response = HttpUtil.httpRequest(HttpUtil.RequestMethodEnum.POST, url, headers, null, null, 10000);
            String content = response.getContent();
            log.info("Transfer token response: {}", response);
            Map<String, String> paramMap = cn.hutool.http.HttpUtil.decodeParamMap(content, StandardCharsets.UTF_8);
            String accessToken = paramMap.get("access_token");
            String scope = paramMap.get("scope");
            String tokenType = paramMap.get("token_type");
            log.info("The token is {}", accessToken);
            UserTokenVo userTokenVo = new UserTokenVo();
            userTokenVo.setAccess_token(accessToken);
            userTokenVo.setScope(scope);
            userTokenVo.setToken_type(tokenType);
            return userTokenVo;
        } catch (Exception e) {
            log.error("GitHub API getToken error: ", e);
            return null;
        }
    }

    private Map<String, String> buildHeader(String token) {
        Map<String, String> headers = new HashMap<>();
        headers.put("Accept", apiAccept);
        if (StringUtils.isNotBlank(token)) {
            headers.put("Authorization", "Bearer " + token);
        }
        headers.put("X-GitHub-Api-Version", apiVersion);
        // GA 加速绑定Host
        headers.put("Host", "api.github.com");
        return headers;
    }
}